﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Command.cs" company="">
//   
// </copyright>
// <summary>
//   The command.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Libium.UseCases
{
    using System;
    using System.Collections.Generic;

    /// <summary>
    ///   The command.
    /// </summary>
    public class Command : ViewBindable, ICommand
    {
        #region Fields

        /// <summary>
        ///   The _arguments.
        /// </summary>
        private readonly Dictionary<string, object> _arguments = new Dictionary<string, object>();

        /// <summary>
        ///   The _definition.
        /// </summary>
        private readonly CommandDefinition _definition;

        /// <summary>
        ///   The _service.
        /// </summary>
        private readonly IUseCaseInstanceService _service;

        /// <summary>
        ///   The _use case.
        /// </summary>
        private readonly IUseCase _useCase;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="Command"/> class.
        /// </summary>
        /// <param name="definition">
        /// The definition. 
        /// </param>
        /// <param name="useCase">
        /// The use case. 
        /// </param>
        /// <param name="service">
        /// The service. 
        /// </param>
        public Command(CommandDefinition definition, IUseCase useCase, IUseCaseInstanceService service)
        {
            _definition = definition;
            _useCase = useCase;
            _service = service;
            // TODO Add unsubscribe on unavailable
				_useCase.EnabledChanged += uc => OnParentEnabledChanged();
				_useCase.ReadonlyChanged += uc => OnParentReadonlyChanged();
        }

        #endregion

        private bool CalculateEnabled(bool parentEnabled, bool parentReadonly)
        {
            return base.Enabled
                && parentEnabled
                && !(IsModifying && parentReadonly);
        }

        private bool CalculateEnabled()
        {
            return CalculateEnabled(_useCase.Enabled, _useCase.ReadOnly);
        }
        /// <summary>
        ///   The on parent enabled changed.
        /// </summary>
        private void OnParentEnabledChanged()
        {
            var previous = CalculateEnabled(!_useCase.Enabled, _useCase.ReadOnly);
            var current = CalculateEnabled();
            if (previous != current)
            {
                OnEnabledChanged();
            }
        }

        /// <summary>
        ///   The on parent enabled changed.
        /// </summary>
        private void OnParentReadonlyChanged()
        {
            var previous = CalculateEnabled(_useCase.Enabled, !_useCase.ReadOnly);
            var current = CalculateEnabled();
            if (previous != current)
            {
                OnEnabledChanged();
            }
        }
        //TODO Code the same as in UseCase, try share
        public override bool Enabled
        {
            get
            {
                return CalculateEnabled();
            }

            set
            {
					if (base.Enabled == value)
                {
                    return;
                }

                var previous = CalculateEnabled();

					 base.Enabled = value;

                var current = CalculateEnabled();

                if (previous != current)
                {
                    OnEnabledChanged();
                }
            }
        }

        #region Public Properties

        /// <summary>
        ///   Gets a value indicating whether activate use case.
        /// </summary>
        /// <exception cref="NotImplementedException"></exception>
        public bool ActivateUseCase
        {
            get
            {
                throw new NotImplementedException();
            }
        }

        /// <summary>
        ///   Gets the description.
        /// </summary>
        public string Description
        {
            get
            {
                return _definition.Description;
            }
        }

        /// <summary>
        ///   Gets the id.
        /// </summary>
        public Guid Id
        {
            get
            {
                return _definition.Id;
            }
        }

        /// <summary>
        ///   Gets a value indicating whether is long running.
        /// </summary>
        /// <exception cref="NotImplementedException"></exception>
        public bool IsLongRunning
        {
            get
            {
                throw new NotImplementedException();
            }
        }

        /// <summary>
        ///   Gets a value indicating whether is modifying.
        /// </summary>
        public bool IsModifying
        {
            get
            {
                return _definition.IsModifying;
            }
        }

        /// <summary>
        ///   Gets the text.
        /// </summary>
        public string Text
        {
            get
            {
                return _definition.Text;
            }
        }

        /// <summary>
        ///   Gets the use case.
        /// </summary>
        public IUseCase UseCase
        {
            get
            {
                return _useCase;
            }
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        ///   The execute.
        /// </summary>
        public void Execute()
        {
            _service.ExecuteCommand(this);
        }

        /// <summary>
        /// The get argument.
        /// </summary>
        /// <param name="name">
        /// The name. 
        /// </param>
        /// <typeparam name="T">
        /// </typeparam>
        /// <returns>
        /// The <see cref="T"/> . 
        /// </returns>
        public T GetArgument<T>(string name)
        {
            return (T)_arguments[name];
        }

        /// <summary>
        /// The set argument.
        /// </summary>
        /// <param name="name">
        /// The name. 
        /// </param>
        /// <param name="value">
        /// The value. 
        /// </param>
        /// <typeparam name="T">
        /// </typeparam>
        public void SetArgument<T>(string name, T value)
        {
            _arguments[name] = value;
        }

        #endregion
    }
}